package com.easycoding.framework.dao.mongodb;


import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

import javax.annotation.Resource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Order;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;

import com.easycoding.framework.entity.mongodb.BaseEntity;
import com.easycoding.framework.util.Guid;
import com.easycoding.framework.util.Tool;
import com.mongodb.BasicDBObject;

public abstract class AbstractDaoSupport<T extends BaseEntity> implements IAbstractDao<T>{
	
	protected final Log logger = LogFactory.getLog(getClass());
	
	@Resource
	private MongoTemplate mongoTemplate;
	

	public T insert(T t){
		if(Tool.isEmpty(t.getId()))
			t.setId(Guid.generate());
		this.getMongoTemplate().save(t);
		return t;
	}
	
	public T update(T t){
		this.getMongoTemplate().save(t);
		return t;
	}
	public T saveOrUpdate(T t){
		if(Tool.isEmpty(t.getId())){
			t.setId(Guid.generate());
		}
		this.getMongoTemplate().save(t);
		return t;
	}
	
	public T findById(String id){
		return this.getMongoTemplate().findById(id, getTClass());
	}
	
	public void deleteById(String id){
		Query query = new Query(Criteria.where("id").is(id));
		this.getMongoTemplate().remove(query, getTClass());
	}
	
	public void delete(T t){
		this.getMongoTemplate().remove(t);
	}
	
	public List<T> findAll(){
		Query query = new Query();
		query.sort().on("dispSeq", Order.ASCENDING);
		query.sort().on("_id", Order.DESCENDING);
		return this.getMongoTemplate().find(new Query(),getTClass());
	}
	
	public List<T> find(Query query){
		return this.getMongoTemplate().find(query,getTClass());
	}
	
	public int findCount(Query query){
		Long k = this.getMongoTemplate().count(query, getTClass());
		return k.intValue();
	}
	
	public void deleteAll(){
		this.getMongoTemplate().remove(new Query(), getTClass());
	}
	
	public void drop(){
		this.getMongoTemplate().dropCollection(getTClass());
	}
	
	/**
	 * 去除引用子项
	 * @param query
	 * @param ref_name
	 * @param ref_collection
	 */
	protected final void removeDBRef(Query query,String ref_name,String ref_id){
		Class<?> entityClass = this.getTClass();
		this.removeDBRef(query, ref_name,  ref_id, entityClass);
	}
	
	/**
	 * 去除引用子项
	 * @param query
	 * @param ref_name
	 * @param ref_id
	 * @param entityClass
	 */
	protected final void removeDBRef(Query query,String ref_name,String ref_id,Class<?> entityClass){
		Class<?> refClass = this.getDBRefClass(entityClass, ref_name);
		String ref_collection_name = this.getRefName(refClass);
		this.removeDBRef(query, ref_name, ref_collection_name, ref_id, entityClass);
	}
	
	/**
	 * 去除引用子项
	 * @param query
	 * @param ref_name
	 * @param ref_collection
	 * @param entityClass
	 */
	protected final void removeDBRef(Query query,String ref_name,String ref_collection_name,String ref_id,Class<?> entityClass){
		Update update = new Update();
		BasicDBObject doc = new BasicDBObject();
		doc.put("$ref", ref_collection_name);
		doc.put("$id", ref_id);
		update.pull(ref_name, doc);
		this.getMongoTemplate().updateMulti(query, update, entityClass);
	}
	
	/**
	 * 增加引用子项
	 * @param query
	 * @param ref_name
	 * @param ref_id
	 */
	protected final void addDBRef(Query query,String ref_name,String ref_id){
		Class<?> entityClass = this.getTClass();
		this.addDBRef(query, ref_name,  ref_id,  entityClass);
	}
	
	/**
	 * 增加引用子项
	 * @param query
	 * @param ref_name
	 * @param ref_id
	 * @param entityClass
	 */
	protected final void addDBRef(Query query,String ref_name,String ref_id,Class<?> entityClass){
		Class<?> refClass = this.getDBRefClass(entityClass, ref_name);
		String ref_collection_name = this.getRefName(refClass);
		this.addDBRef(query, ref_name, ref_collection_name, ref_id, refClass, entityClass);
	}
	
	/**
	 * 增加引用子项
	 * @param query
	 * @param ref_name
	 * @param ref_collection
	 * @param id
	 * @param refClass
	 * @param entityClass
	 */
	protected final void addDBRef(Query query,String ref_name,String ref_collection_name,String ref_id,Class<?> refClass,Class<?> entityClass){
		if(this.getMongoTemplate().findById(ref_id, refClass) != null){
			Update update = new Update();
			BasicDBObject doc = new BasicDBObject();
			doc.put("$ref", ref_collection_name);
			doc.put("$id", ref_id);
			update.push(ref_name, doc);
			this.getMongoTemplate().updateMulti(query, update, entityClass);
		}
	}
	
	/**
	 * 
	 * @param query
	 * @param ref
	 * @param id
	 * @param entityClass
	 */
	protected final void clearDBRef(Query query,String ref,Class<?> entityClass){
		Update update = new Update();
		update.unset(ref);
		this.getMongoTemplate().updateMulti(query, update, entityClass);
	}
	
	private Class<?> getDBRefClass(Class<?> entityClass,String fileName){
		try{
			Field f = entityClass.getDeclaredField(fileName);
			Class<?> classz  = f.getType();
			if(classz.isAssignableFrom(List.class)) //【2】  
		    {   
		            Type fc = f.getGenericType(); // 关键的地方，如果是List类型，得到其Generic的类型  
		            if(fc instanceof ParameterizedType) // 【3】如果是泛型参数的类型   
		            {   
		                   ParameterizedType pt = (ParameterizedType) fc;  
		                   Class<?> genericClazz = (Class<?>)pt.getActualTypeArguments()[0]; //【4】 得到泛型里的class类型对象。  
		                   System.out.print(genericClazz);
		                   return genericClazz;
		             }   
		      }
			else
				return f.getClass();
		}
		catch(Exception e){
			logger.error("获取引用类型出错",e);
		}
		return null;
	}
	
	@SuppressWarnings("unchecked")
	private Class<T> getTClass(){
		return (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
	}
	
	private String getRefName(Class<?> classz){
		this.getMongoTemplate().getConverter().getMappingContext();
		MongoPersistentEntity<?> entity = this.getMongoTemplate().getConverter().getMappingContext().getPersistentEntity(classz);
		if (entity == null) {
			throw new InvalidDataAccessApiUsageException("No Persitent Entity information found for the class "
					+ classz.getName());
		}
		return entity.getCollection();
	}
	
	public void setMongoTemplate(MongoTemplate mongoTemplate) {
		this.mongoTemplate = mongoTemplate;
	}

	public MongoTemplate getMongoTemplate() {
		return mongoTemplate;
	}
}
